#!/usr/bin/env ruby

LKP_SRC = ENV['LKP_SRC'] || File.dirname(File.dirname(File.dirname(File.realpath($PROGRAM_NAME))))

require "#{LKP_SRC}/lib/log"
require "#{LKP_SRC}/lib/string"

class StressorStater
  def stat(line)
    if line =~ /stress-ng: (info|metrc):.*\((average|geometr?ic mean|harmonic mean)/
      stat_miscellaneous_metrics(line)
    else
      stat_major_metrics(line)
    end
  end

  private

  def stat_major_metrics(line)
    items = line.split

    # stress-ng: info:  [4372] stressor       bogo ops real time  usr time  sys time   bogo ops/s     bogo ops/s
    # stress-ng: info:  [4372]                           (secs)    (secs)    (secs)   (real time) (usr+sys time)
    # stress-ng: info:  [4372] flock         470916710     60.00    128.04   1291.13   7848529.59      331824.84
    #
    # stress-ng: metrc: [4382] stressor       bogo ops real time  usr time  sys time   bogo ops/s     bogo ops/s CPU used per       RSS Max
    # stress-ng: metrc: [4382]                           (secs)    (secs)    (secs)   (real time) (usr+sys time) instance (%)          (KB)
    # stress-ng: metrc: [4382] flock         171659332     60.00     44.80    771.49   2860967.69      210292.23       226.75          2564
    return unless items.size == 10 || items.size == 12

    stressor = items[3]
    bogo_ops = items[4]

    # stress-ng: info:  [2659] splice                0      0.00      0.00      0.00         0.00         0.00
    if bogo_ops == '0'
      puts "#{stressor}.fail: 1"
    else
      puts "#{stressor}.pass: 1"
      puts "#{stressor}.ops: #{bogo_ops}"
      puts "#{stressor}.ops_per_sec: #{items[8]}"
    end
  end

  def stat_miscellaneous_metrics(line)
    # stress-ng: info:  [5865] memrate             2190.96 write128nt MB per sec (geometic mean)
    # stress-ng: info:  [2680] memrate         1656.82 write128 MB/sec (average per stressor)
    # stress-ng: info:  [2759] pipeherd           1.01 context switches per bogo op (average per stressor)
    # stress-ng: info:  [2792] stream          2111.71 memory rate (Mflop per sec) (average per stressor)
    # stress-ng: info:  [5705] matrix-3d             60.43 add matrix-3d ops per sec (geometic mean of 128 instances)
    # stress-ng: metrc: [5237] udp-flood              0.04 MB per sec sendto rate (geometric mean of 128 instances)
    # stress-ng: metrc: [5237] udp-flood           1097.62 sendto calls per sec (geometric mean of 128 instances)
    # stress-ng: metrc: [5237] udp-flood            100.00 % sendto calls succeeded (geometric mean of 128 instances)
    # stress-ng: metrc: [4382] flock               1883.42 nanosecs per flock unlock call (harmonic mean of 6 instances)
    items = line.sub(/ \((average|geometr?ic mean|harmonic mean).*\)$/, '')
                .split

    # unit is "write128_MB/sec" and it has variable length
    unit = items[5..-1].join('_')

    stressor = items[3]

    if items[4] == '0'
      puts "#{stressor}.#{unit}.fail:"
    else
      puts "#{stressor}.#{unit}.pass: 1"
      puts "#{stressor}.#{unit}: #{items[4]}"
    end
  end
end

stater = nil

while (line = $stdin.gets)
  line = line.resolve_invalid_bytes

  case line
  when /(this stressor is not implemented on this system)/,
       /(No stress workers invoked)/,
       /(all bogo-op counters are zero, data may be incorrect)/
    # stress-ng: warn:  [4124] metrics-check: all bogo-op counters are zero, data may be incorrect
    log_warn $1
    exit 1
  when /stress-ng: fail:.*sigaction signal/
    # stress-ng: fail:  [5780] stress-ng: sigaction signal 6 'SIGABRT': errno=22 (Invalid argument)
    puts 'sigaction.fail: 1'
    break
  when /fail:/, /warn:/
    # stress-ng: fail:  [2694] qsort instance 51 corrupted bogo-ops counter, 18298 vs 0
    # stress-ng: fail:  [2694] qsort instance 51 hash error in bogo-ops counter and run flag, 3808369776 vs 0
    # stress-ng: fail:  [2694] metrics-check: stressor metrics corrupted, data is compromised
    #
    # There's nothing need to do for other fail/warn now
    next
  when /\(secs\)\s+\(secs\)\s+\(secs\)/
    # stress-ng: info:  [2694] stressor       bogo ops real time  usr time  sys time   bogo ops/s   bogo ops/s
    # stress-ng: info:  [2694]                           (secs)    (secs)    (secs)   (real time) (usr+sys time)
    # stress-ng: info:  [2694] atomic          1060503     10.00    928.24      0.01    106049.80      1142.48
    stater = StressorStater.new
  when /run time:/
    # stress-ng: info:  [2694] for a 494.74s run time:
    # stress-ng: info:  [2694]   47495.22s available CPU time
    # stress-ng: info:  [2694]   25864.19s user time   ( 54.46%)
    # stress-ng: info:  [2694]   13804.50s system time ( 29.07%)
    # stress-ng: info:  [2694]   39668.69s total time  ( 83.52%)
    # stress-ng: info:  [2694] load average: 157.53 303.16 161.49
    break
  else
    begin
      stater.stat(line) if stater
    rescue StandardError => e
      log_warn e.message
      exit 1
    end
  end
end
